#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
音频播放模块
Audio Player Module

提供音频文件播放和系统提示音功能
Provides audio file playback and system sound functionality
"""

import os
import threading
import time
import logging
from typing import Optional, Dict, Any, List, Callable
import wave
import pyaudio
from ctypes import *
from contextlib import contextmanager
# 修复导入问题，使用绝对导入
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
    from create_system_sounds import create_beep, create_success_sound, create_error_sound, create_wake_sound
except ImportError:
    # 如果导入失败，定义空函数避免错误
    def create_beep(*args, **kwargs): pass
    def create_success_sound(*args, **kwargs): pass
    def create_error_sound(*args, **kwargs): pass
    def create_wake_sound(*args, **kwargs): pass


def create_pyaudio_instance():
    """
    创建PyAudio实例，处理Linux环境下的invoke_excepthook错误和设备验证问题
    Create PyAudio instance, handling invoke_excepthook errors and device validation issues in Linux environment
    """
    try:
        # 保存原始的异常处理器
        original_excepthook = sys.excepthook
        
        # 临时禁用异常处理器以避免PyAudio的invoke_excepthook错误
        sys.excepthook = lambda exc_type, exc_value, exc_traceback: None
        
        # 设置环境变量以减少ALSA错误
        os.environ['ALSA_CARD'] = '0'
        os.environ['ALSA_DEVICE'] = '0'
        
        # 创建PyAudio实例
        p = pyaudio.PyAudio()
        
        # 验证PyAudio实例是否正常工作
        try:
            device_count = p.get_device_count()
            if device_count == 0:
                raise RuntimeError("No audio devices found")
        except Exception as validation_error:
            p.terminate()
            raise RuntimeError(f"PyAudio validation failed: {validation_error}")
        
        # 恢复原始异常处理器
        sys.excepthook = original_excepthook
        
        return p
    except Exception as e:
        # 确保恢复原始异常处理器
        sys.excepthook = original_excepthook
        raise e
    def create_wake_sound(*args, **kwargs): pass


# ALSA错误抑制器
@contextmanager
def suppress_alsa_errors():
    """
    抑制ALSA错误输出的上下文管理器
    Context manager to suppress ALSA error output
    """
    try:
        # 尝试抑制ALSA错误输出
        ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
        
        def py_error_handler(filename, line, function, err, fmt):
            # 静默处理ALSA错误
            pass
        
        c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
        
        # 尝试获取ALSA库
        try:
            asound = cdll.LoadLibrary('libasound.so.2')
            asound.snd_lib_error_set_handler(c_error_handler)
        except (OSError, AttributeError):
            # 如果无法加载ALSA库，设置环境变量作为备选方案
            os.environ['ALSA_PCM_CARD'] = 'default'
            os.environ['ALSA_PCM_DEVICE'] = '0'
        
        yield
        
    except Exception:
        # 如果抑制失败，至少设置环境变量
        os.environ['ALSA_VERBOSITY'] = '0'
        yield
    finally:
        try:
            # 恢复默认错误处理器
            if 'asound' in locals():
                asound.snd_lib_error_set_handler(None)
        except:
            pass


class AudioPlayer:
    """
    音频播放器类
    Audio player class
    
    支持WAV文件播放和系统提示音管理
    Supports WAV file playback and system sound management
    """
    
    def __init__(self, config: Dict[str, Any]):
        """
        初始化音频播放器
        Initialize audio player
        
        Args:
            config (Dict[str, Any]): 配置参数 / Configuration parameters
                - system_sounds_dir: 系统提示音目录 / System sounds directory
                - default_volume: 默认音量 0.0-1.0 / Default volume 0.0-1.0
                - output_device_index: 输出设备索引 / Output device index
        """
        self.config = config
        self.system_sounds_dir = config.get('system_sounds_dir', './audio/system_sounds')
        self.default_volume = config.get('default_volume', 0.8)
        
        # 动态检测USB Audio Device
        configured_index = config.get('output_device_index', None)
        if configured_index == 'auto_usb':
            self.output_device_index = self._find_usb_audio_device()
        else:
            self.output_device_index = configured_index
            
        # 验证设备索引的有效性
        if self.output_device_index is not None:
            self.output_device_index = self._validate_device_index(self.output_device_index)
            
        self.auto_heal_system_sounds = config.get('auto_heal_system_sounds', True)
        
        # 播放状态管理
        self.is_playing = False
        self.current_file: Optional[str] = None
        self.play_thread: Optional[threading.Thread] = None
        
        # 设置日志（需要在其他初始化之前）
        self.logger = logging.getLogger(__name__)
        
        # 设置ALSA错误抑制
        self._setup_alsa_error_suppression()
        
        # 系统提示音映射
        self.system_sounds = {}
        self._load_system_sounds()
        
        # 回调函数
        self.play_start_callback: Optional[Callable] = None
        self.play_end_callback: Optional[Callable] = None
        
        # 统计信息
        self.play_count = 0
        self.total_play_time = 0.0
            
    def _setup_alsa_error_suppression(self):
        """
        设置ALSA错误抑制
        Setup ALSA error suppression
        """
        try:
            # 设置环境变量来抑制ALSA错误
            os.environ['ALSA_VERBOSITY'] = '0'
            os.environ['ALSA_PCM_CARD'] = 'default'
            os.environ['ALSA_PCM_DEVICE'] = '0'
            
            # 尝试通过ctypes抑制ALSA错误
            try:
                ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
                
                def py_error_handler(filename, line, function, err, fmt):
                    # 静默处理ALSA错误，只记录严重错误
                    if err and err < -10:  # 只记录严重错误
                        self.logger.debug(f"ALSA error suppressed: {err}")
                
                c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
                
                # 尝试加载ALSA库并设置错误处理器
                asound = cdll.LoadLibrary('libasound.so.2')
                asound.snd_lib_error_set_handler(c_error_handler)
                self.logger.debug("ALSA error suppression enabled via ctypes")
                
            except (OSError, AttributeError) as e:
                self.logger.debug(f"Could not set ALSA error handler via ctypes: {e}")
                
        except Exception as e:
            self.logger.debug(f"ALSA error suppression setup failed: {e}")
            
    def _load_system_sounds(self):
        """
        加载系统提示音文件并在需要时自愈
        Load system sound files and self-heal when necessary
        """
        # 确保目录存在
        if not os.path.exists(self.system_sounds_dir):
            os.makedirs(self.system_sounds_dir, exist_ok=True)
            self.logger.warning(f"System sounds directory created: {self.system_sounds_dir}")
        
        expected_names = [
            'beep', 'success', 'error', 'wake_up', 'start_record', 'stop_record'
        ]
        
        # 先扫描已有文件，并在开启自愈时校验有效性
        self.system_sounds = {}
        for filename in os.listdir(self.system_sounds_dir):
            if filename.lower().endswith('.wav'):
                name = os.path.splitext(filename)[0]
                filepath = os.path.join(self.system_sounds_dir, filename)
                if self.auto_heal_system_sounds and not self._is_valid_wav(filepath):
                    self.logger.warning(f"Invalid WAV detected, regenerating: {filepath}")
                    self._ensure_system_sound(name)
                else:
                    self.system_sounds[name] = filepath
        
        # 确保必需的系统提示音存在（按需生成）
        if self.auto_heal_system_sounds:
            for name in expected_names:
                self._ensure_system_sound(name)
        
        self.logger.info(f"Loaded {len(self.system_sounds)} system sounds")
        
    def set_callbacks(self, 
                     play_start_callback: Optional[Callable] = None,
                     play_end_callback: Optional[Callable] = None):
        """
        设置播放回调函数
        Set playback callback functions
        
        Args:
            play_start_callback: 播放开始回调 / Play start callback
            play_end_callback: 播放结束回调 / Play end callback
        """
        self.play_start_callback = play_start_callback
        self.play_end_callback = play_end_callback
        
    def play_file(self, filepath: str, volume: Optional[float] = None, blocking: bool = False) -> bool:
        """
        播放音频文件
        Play audio file
        
        Args:
            filepath: 音频文件路径 / Audio file path
            volume: 音量 0.0-1.0 / Volume 0.0-1.0
            blocking: 是否阻塞播放 / Whether to play blocking
            
        Returns:
            bool: 是否成功开始播放 / Whether successfully started playing
        """
        if not os.path.exists(filepath):
            self.logger.error(f"Audio file not found: {filepath}")
            return False
            
        if volume is None:
            volume = self.default_volume
            
        # 直接播放
        return self._play_wav_file(filepath, volume, blocking)
            
    def play_system_sound(self, sound_name: str, volume: Optional[float] = None, blocking: bool = False) -> bool:
        """
        播放系统提示音
        Play system sound
        
        Args:
            sound_name: 系统提示音名称 / System sound name
            volume: 音量 0.0-1.0 / Volume 0.0-1.0
            blocking: 是否阻塞播放 / Whether to play blocking
            
        Returns:
            bool: 是否成功开始播放 / Whether successfully started playing
        """
        if sound_name not in self.system_sounds:
            # 尝试自愈生成后再播放
            if self.auto_heal_system_sounds and self._ensure_system_sound(sound_name):
                pass
            else:
                self.logger.error(f"System sound not found: {sound_name}")
                self.logger.info(f"Available system sounds: {list(self.system_sounds.keys())}")
                return False
        
        filepath = self.system_sounds[sound_name]
        # 播放前再次校验文件有效性，必要时自愈
        if self.auto_heal_system_sounds and not self._is_valid_wav(filepath):
            self.logger.warning(f"System sound file corrupted, regenerating: {filepath}")
            if not self._ensure_system_sound(sound_name):
                return False
            filepath = self.system_sounds.get(sound_name, filepath)
        
        return self.play_file(filepath, volume, blocking)
        
    def play_beep(self, frequency: int = 1000, duration: float = 0.5, volume: Optional[float] = None) -> bool:
        """
        播放蜂鸣音
        Play beep sound
        
        Args:
            frequency: 频率 Hz / Frequency in Hz
            duration: 持续时间 秒 / Duration in seconds
            volume: 音量 0.0-1.0 / Volume 0.0-1.0
            
        Returns:
            bool: 是否成功播放 / Whether successfully played
        """
        if volume is None:
            volume = self.default_volume
            
        return self._generate_and_play_beep(frequency, duration, volume)
            
    def _play_wav_file(self, filepath: str, volume: float, blocking: bool) -> bool:
        """
        播放WAV文件的内部方法
        Internal method to play WAV file
        
        Args:
            filepath: 文件路径 / File path
            volume: 音量 / Volume
            blocking: 是否阻塞 / Whether blocking
            
        Returns:
            bool: 是否成功播放 / Whether successfully played
        """
        if blocking:
            return self._play_wav_blocking(filepath, volume)
        else:
            if self.is_playing:
                self.logger.warning("Another audio is currently playing")
                return False
                
            self.play_thread = threading.Thread(
                target=self._play_wav_blocking,
                args=(filepath, volume),
                daemon=True
            )
            self.play_thread.start()
            return True
            
    def _play_wav_blocking(self, filepath: str, volume: float) -> bool:
        """
        阻塞方式播放WAV文件
        Play WAV file in blocking mode
        
        Args:
            filepath: 文件路径 / File path
            volume: 音量 / Volume
            
        Returns:
            bool: 是否成功播放 / Whether successfully played
        """
        self.is_playing = True
        self.current_file = filepath
        start_time = time.time()
        
        # 调用开始回调
        if self.play_start_callback:
            try:
                self.play_start_callback(filepath)
            except Exception as e:
                self.logger.error(f"Error in play start callback: {e}")
                
        try:
            # 打开WAV文件
            with wave.open(filepath, 'rb') as wf:
                # 获取音频参数
                channels = wf.getnchannels()
                sample_width = wf.getsampwidth()
                original_framerate = wf.getframerate()
                frames = wf.getnframes()
                
                self.logger.info(f"Original audio: {channels}ch, {sample_width}bytes, {original_framerate}Hz")
                
                # 初始化PyAudio（抑制ALSA错误输出）
                with suppress_alsa_errors():
                    p = create_pyaudio_instance()
                
                # 尝试找到可用的音频设备和采样率
                stream, actual_framerate = self._create_audio_stream_with_rate(p, channels, sample_width, original_framerate)
                
                self.logger.info(f"Using audio stream: {channels}ch, {sample_width}bytes, {actual_framerate}Hz")
                
                # 检查是否需要重采样
                need_resample = (original_framerate != actual_framerate)
                
                # 获取流的缓冲区大小信息
                try:
                    stream_info = stream.get_output_latency()
                    self.logger.debug(f"Stream output latency: {stream_info}")
                except:
                    pass
                
                if need_resample:
                    self.logger.warning(f"Resampling from {original_framerate}Hz to {actual_framerate}Hz")
                    # 读取所有音频数据进行重采样
                    audio_data = wf.readframes(frames)
                    resampled_data = self._resample_audio(audio_data, channels, sample_width, 
                                                        original_framerate, actual_framerate)
                    
                    # 播放重采样后的数据，使用更大的chunk以减少断续
                    # 计算合适的chunk大小：约100ms的数据
                    chunk_frames = max(1024, int(actual_framerate * 0.1))
                    chunk_size = chunk_frames * sample_width * channels
                    
                    for i in range(0, len(resampled_data), chunk_size):
                        chunk = resampled_data[i:i + chunk_size]
                        if volume != 1.0:
                            chunk = self._apply_volume(chunk, volume, sample_width)
                        stream.write(chunk)
                        
                        # 检查是否需要停止播放
                        if not self.is_playing:
                            break
                else:
                    # 直接播放原始数据，使用更大的chunk
                    # 计算合适的chunk大小：约100ms的数据
                    chunk_frames = max(1024, int(original_framerate * 0.1))
                    
                    data = wf.readframes(chunk_frames)
                    
                    while data and self.is_playing:
                        if volume != 1.0:
                            data = self._apply_volume(data, volume, sample_width)
                        stream.write(data)
                        data = wf.readframes(chunk_frames)
                    
                # 清理资源
                stream.stop_stream()
                stream.close()
                p.terminate()
                
            # 更新统计信息
            play_time = time.time() - start_time
            self.play_count += 1
            self.total_play_time += play_time
            
            self.logger.info(f"Successfully played: {os.path.basename(filepath)} ({play_time:.2f}s)")
            success = True
            
        except Exception as e:
            self.logger.error(f"Error playing audio file {filepath}: {e}")
            success = False
        finally:
            self.is_playing = False
            self.current_file = None
            
            # 调用结束回调
            if self.play_end_callback:
                try:
                    self.play_end_callback(filepath, success)
                except Exception as e:
                    self.logger.error(f"Error in play end callback: {e}")
                    
        return success
    
    def _create_audio_stream_with_rate(self, p: pyaudio.PyAudio, channels: int, sample_width: int, framerate: int):
        """
        创建音频流，支持设备检测和采样率回退，返回实际使用的采样率
        Create audio stream with device detection and sample rate fallback, return actual sample rate
        
        Args:
            p: PyAudio实例
            channels: 声道数
            sample_width: 采样宽度
            framerate: 期望采样率
            
        Returns:
            tuple: (PyAudio stream对象, 实际采样率)
        """
        # 支持的采样率列表（按优先级排序）
        supported_rates = [framerate, 44100, 48000, 22050, 16000, 8000]
        
        # 获取可用的输出设备
        available_devices = []
        for i in range(p.get_device_count()):
            try:
                device_info = p.get_device_info_by_index(i)
                if device_info['maxOutputChannels'] > 0:
                    available_devices.append((i, device_info['name']))
            except:
                continue
        
        self.logger.info(f"Available audio devices: {available_devices}")
        
        # 尝试的设备列表
        device_indices = [self.output_device_index] if self.output_device_index is not None else [None]
        device_indices.extend([dev[0] for dev in available_devices if dev[0] != self.output_device_index])
        
        last_error = None
        
        # 尝试不同的设备和采样率组合
        for device_index in device_indices:
            # 验证设备索引（如果不是None）
            if device_index is not None:
                # 检查设备索引是否在有效范围内
                try:
                    device_count = p.get_device_count()
                    if device_index >= device_count or device_index < 0:
                        self.logger.warning(f"Skipping invalid device index {device_index} (range: 0-{device_count-1})")
                        continue
                    
                    # 检查设备是否支持输出
                    device_info = p.get_device_info_by_index(device_index)
                    if device_info['maxOutputChannels'] == 0:
                        self.logger.warning(f"Skipping device {device_index} (no output channels)")
                        continue
                        
                except Exception as device_error:
                    self.logger.warning(f"Error checking device {device_index}: {device_error}")
                    continue
            
            for rate in supported_rates:
                try:
                    # 计算合适的缓冲区大小
                    # 目标：约50ms的缓冲区，但不少于512帧，不多于4096帧
                    frames_per_buffer = max(512, min(4096, int(rate * 0.05)))
                    
                    stream_kwargs = {
                        'format': p.get_format_from_width(sample_width),
                        'channels': channels,
                        'rate': rate,
                        'output': True,
                        'frames_per_buffer': frames_per_buffer
                    }
                    
                    if device_index is not None:
                        stream_kwargs['output_device_index'] = device_index
                    
                    # 使用ALSA错误抑制器尝试打开流
                    with suppress_alsa_errors():
                        stream = p.open(**stream_kwargs)
                    
                    if rate != framerate:
                        self.logger.warning(f"Using fallback sample rate {rate}Hz instead of {framerate}Hz")
                    if device_index is not None and device_index != self.output_device_index:
                        device_name = next((name for idx, name in available_devices if idx == device_index), f"Device {device_index}")
                        self.logger.warning(f"Using fallback audio device: {device_name}")
                    
                    return stream, rate
                    
                except Exception as e:
                    last_error = e
                    self.logger.debug(f"Failed to create stream with device {device_index}, rate {rate}Hz: {e}")
                    continue
        
        # 如果所有尝试都失败了，抛出最后一个错误
        raise last_error if last_error else Exception("No compatible audio device found")
    
    def _resample_audio(self, audio_data: bytes, channels: int, sample_width: int, 
                       original_rate: int, target_rate: int) -> bytes:
        """
        音频重采样
        Audio resampling
        
        Args:
            audio_data: 原始音频数据
            channels: 声道数
            sample_width: 采样宽度
            original_rate: 原始采样率
            target_rate: 目标采样率
            
        Returns:
            bytes: 重采样后的音频数据
        """
        try:
            import numpy as np
            from scipy import signal
            
            # 将字节数据转换为numpy数组
            if sample_width == 1:
                dtype = np.uint8
            elif sample_width == 2:
                dtype = np.int16
            elif sample_width == 4:
                dtype = np.int32
            else:
                raise ValueError(f"Unsupported sample width: {sample_width}")
            
            # 转换为numpy数组
            audio_array = np.frombuffer(audio_data, dtype=dtype)
            
            # 如果是多声道，重新整形
            if channels > 1:
                audio_array = audio_array.reshape(-1, channels)
            
            # 计算重采样比例
            resample_ratio = target_rate / original_rate
            new_length = int(len(audio_array) * resample_ratio)
            
            # 执行重采样
            if channels == 1:
                resampled = signal.resample(audio_array, new_length)
            else:
                # 对每个声道分别重采样
                resampled = np.zeros((new_length, channels), dtype=audio_array.dtype)
                for ch in range(channels):
                    resampled[:, ch] = signal.resample(audio_array[:, ch], new_length)
            
            # 转换回原始数据类型并返回字节数据
            resampled = np.clip(resampled, np.iinfo(dtype).min, np.iinfo(dtype).max)
            return resampled.astype(dtype).tobytes()
            
        except ImportError:
            self.logger.warning("scipy not available, using simple linear interpolation for resampling")
            return self._simple_resample(audio_data, channels, sample_width, original_rate, target_rate)
        except Exception as e:
            self.logger.error(f"Error during resampling: {e}")
            return audio_data  # 返回原始数据作为回退
    
    def _simple_resample(self, audio_data: bytes, channels: int, sample_width: int,
                        original_rate: int, target_rate: int) -> bytes:
        """
        简单的线性插值重采样（当scipy不可用时的回退方案）
        Simple linear interpolation resampling (fallback when scipy is not available)
        """
        try:
            import numpy as np
            
            # 将字节数据转换为numpy数组
            if sample_width == 1:
                dtype = np.uint8
            elif sample_width == 2:
                dtype = np.int16
            elif sample_width == 4:
                dtype = np.int32
            else:
                return audio_data  # 不支持的格式，返回原始数据
            
            audio_array = np.frombuffer(audio_data, dtype=dtype)
            
            # 计算新的长度
            resample_ratio = target_rate / original_rate
            new_length = int(len(audio_array) * resample_ratio)
            
            # 简单的线性插值
            old_indices = np.arange(len(audio_array))
            new_indices = np.linspace(0, len(audio_array) - 1, new_length)
            resampled = np.interp(new_indices, old_indices, audio_array.astype(np.float32))
            
            # 转换回原始数据类型
            resampled = np.clip(resampled, np.iinfo(dtype).min, np.iinfo(dtype).max)
            return resampled.astype(dtype).tobytes()
            
        except Exception as e:
            self.logger.error(f"Error during simple resampling: {e}")
            return audio_data  # 返回原始数据
    
    def _apply_volume(self, audio_data: bytes, volume: float, sample_width: int) -> bytes:
        """
        应用音量控制
        Apply volume control
        
        Args:
            audio_data: 音频数据
            volume: 音量倍数 (0.0-1.0)
            sample_width: 采样宽度
            
        Returns:
            bytes: 调整音量后的音频数据
        """
        try:
            import numpy as np
            
            if sample_width == 1:
                dtype = np.uint8
                audio_array = np.frombuffer(audio_data, dtype=dtype).astype(np.float32)
                audio_array = (audio_array - 128) * volume + 128
                audio_array = np.clip(audio_array, 0, 255)
                return audio_array.astype(dtype).tobytes()
            elif sample_width == 2:
                dtype = np.int16
                audio_array = np.frombuffer(audio_data, dtype=dtype).astype(np.float32)
                audio_array = audio_array * volume
                audio_array = np.clip(audio_array, -32768, 32767)
                return audio_array.astype(dtype).tobytes()
            elif sample_width == 4:
                dtype = np.int32
                audio_array = np.frombuffer(audio_data, dtype=dtype).astype(np.float32)
                audio_array = audio_array * volume
                audio_array = np.clip(audio_array, -2147483648, 2147483647)
                return audio_array.astype(dtype).tobytes()
            else:
                return audio_data  # 不支持的格式，返回原始数据
                
        except Exception as e:
            self.logger.error(f"Error applying volume: {e}")
            return audio_data  # 返回原始数据
    
    def _create_audio_stream(self, p: pyaudio.PyAudio, channels: int, sample_width: int, framerate: int):
        """
        创建音频流，支持设备检测和采样率回退
        Create audio stream with device detection and sample rate fallback
        
        Args:
            p: PyAudio实例
            channels: 声道数
            sample_width: 采样宽度
            framerate: 采样率
            
        Returns:
            PyAudio stream对象
        """
        # 支持的采样率列表（按优先级排序）
        supported_rates = [framerate, 44100, 48000, 22050, 16000, 8000]
        
        # 获取可用的输出设备
        available_devices = []
        for i in range(p.get_device_count()):
            try:
                device_info = p.get_device_info_by_index(i)
                if device_info['maxOutputChannels'] > 0:
                    available_devices.append((i, device_info['name']))
            except:
                continue
        
        self.logger.info(f"Available audio devices: {available_devices}")
        
        # 尝试的设备列表
        device_indices = [self.output_device_index] if self.output_device_index is not None else [None]
        device_indices.extend([dev[0] for dev in available_devices if dev[0] != self.output_device_index])
        
        last_error = None
        
        # 尝试不同的设备和采样率组合
        for device_index in device_indices:
            for rate in supported_rates:
                try:
                    # 计算合适的缓冲区大小
                    # 目标：约50ms的缓冲区，但不少于512帧，不多于4096帧
                    frames_per_buffer = max(512, min(4096, int(rate * 0.05)))
                    
                    stream_kwargs = {
                        'format': p.get_format_from_width(sample_width),
                        'channels': channels,
                        'rate': rate,
                        'output': True,
                        'frames_per_buffer': frames_per_buffer
                    }
                    
                    if device_index is not None:
                        stream_kwargs['output_device_index'] = device_index
                    
                    # 尝试打开流
                    stream = p.open(**stream_kwargs)
                    
                    if rate != framerate:
                        self.logger.warning(f"Using fallback sample rate {rate}Hz instead of {framerate}Hz")
                    if device_index is not None and device_index != self.output_device_index:
                        device_name = next((name for idx, name in available_devices if idx == device_index), f"Device {device_index}")
                        self.logger.warning(f"Using fallback audio device: {device_name}")
                    
                    return stream
                    
                except Exception as e:
                    last_error = e
                    self.logger.debug(f"Failed to open stream with device {device_index}, rate {rate}Hz: {e}")
                    continue
        
        # 如果所有尝试都失败了，抛出最后一个错误
        raise last_error if last_error else Exception("No compatible audio device found")
        
    def _generate_and_play_beep(self, frequency: int, duration: float, volume: float) -> bool:
        """
        生成并播放蜂鸣音
        Generate and play beep sound
        
        Args:
            frequency: 频率 / Frequency
            duration: 持续时间 / Duration
            volume: 音量 / Volume
            
        Returns:
            bool: 是否成功播放 / Whether successfully played
        """
        try:
            import numpy as np
            
            sample_rate = 44100
            frames = int(duration * sample_rate)
            
            # 生成正弦波
            t = np.linspace(0, duration, frames, False)
            wave_data = np.sin(2 * np.pi * frequency * t) * volume
            
            # 转换为16位整数
            wave_data = (wave_data * 32767).astype(np.int16)
            
            # 播放音频（抑制ALSA错误输出）
            with suppress_alsa_errors():
                # 初始化PyAudio
                try:
                    p = create_pyaudio_instance()
                except Exception as e:
                    self.logger.error(f"Failed to initialize PyAudio: {e}")
                    return False
            
            # 计算合适的缓冲区大小
            frames_per_buffer = max(512, min(4096, int(sample_rate * 0.05)))
            
            stream = p.open(
                format=pyaudio.paInt16,
                channels=1,
                rate=sample_rate,
                output=True,
                output_device_index=self.output_device_index,
                frames_per_buffer=frames_per_buffer
            )
            
            # 分块播放以避免断续
            audio_bytes = wave_data.tobytes()
            chunk_size = frames_per_buffer * 2  # 2 bytes per sample for 16-bit
            
            for i in range(0, len(audio_bytes), chunk_size):
                chunk = audio_bytes[i:i + chunk_size]
                stream.write(chunk)
            
            stream.stop_stream()
            stream.close()
            p.terminate()
            
            self.logger.info(f"Played beep: {frequency}Hz for {duration}s")
            return True
            
        except ImportError:
            self.logger.warning("NumPy not available, beep generation disabled")
            return False
        except Exception as e:
            self.logger.error(f"Error generating beep: {e}")
            return False
            

                
    def stop_playback(self):
        """
        停止当前播放
        Stop current playback
        """
        # 注意：这里只是设置标志，实际的停止需要在播放循环中检查
        self.is_playing = False
        

            
    def _validate_device_index(self, device_index: int) -> Optional[int]:
        """
        验证设备索引的有效性
        Validate device index validity
        
        Args:
            device_index: 要验证的设备索引
            
        Returns:
            Optional[int]: 有效的设备索引，如果无效则返回None
        """
        try:
            with suppress_alsa_errors():
                p = create_pyaudio_instance()
                try:
                    # 检查设备索引是否在有效范围内
                    device_count = p.get_device_count()
                    if device_index >= device_count or device_index < 0:
                        if hasattr(self, 'logger'):
                            self.logger.warning(f"Device index {device_index} is out of range (0-{device_count-1}), using default device")
                        return None
                    
                    # 检查设备是否支持输出
                    device_info = p.get_device_info_by_index(device_index)
                    if device_info['maxOutputChannels'] == 0:
                        if hasattr(self, 'logger'):
                            self.logger.warning(f"Device {device_index} does not support output, using default device")
                        return None
                    
                    # 尝试创建一个测试流来验证设备是否可用
                    try:
                        test_stream = p.open(
                            format=pyaudio.paInt16,
                            channels=1,
                            rate=44100,
                            output=True,
                            output_device_index=device_index,
                            frames_per_buffer=1024
                        )
                        test_stream.close()
                        
                        if hasattr(self, 'logger'):
                            self.logger.info(f"Device index {device_index} validated successfully: {device_info['name']}")
                        return device_index
                        
                    except Exception as stream_error:
                        if hasattr(self, 'logger'):
                            self.logger.warning(f"Device {device_index} failed stream test: {stream_error}, using default device")
                        return None
                        
                finally:
                    p.terminate()
                    
        except Exception as e:
            if hasattr(self, 'logger'):
                self.logger.error(f"Error validating device index {device_index}: {e}")
            return None
    
    def _find_usb_audio_device(self) -> Optional[int]:
        """
        动态查找USB Audio Device的索引
        Dynamically find USB Audio Device index
        
        Returns:
            Optional[int]: USB Audio Device的索引，如果未找到则返回None
        """
        try:
            devices = self.get_available_output_devices()
            
            # 查找包含"USB Audio"关键词的设备
            for device_index, device_name in devices.items():
                if 'USB Audio' in device_name or 'usb' in device_name.lower():
                    if hasattr(self, 'logger'):
                        self.logger.info(f"Found USB Audio Device at index {device_index}: {device_name}")
                    return device_index
            
            # 如果没找到USB Audio Device，记录警告并返回None使用默认设备
            if hasattr(self, 'logger'):
                self.logger.warning("USB Audio Device not found, using default audio device")
            return None
            
        except Exception as e:
            if hasattr(self, 'logger'):
                self.logger.error(f"Error finding USB Audio Device: {e}")
            return None
    
    def get_available_output_devices(self) -> Dict[int, str]:
        """
        获取可用的音频输出设备
        Get available audio output devices
        
        Returns:
            Dict[int, str]: 设备索引和名称的映射 / Mapping of device index to name
        """
        with suppress_alsa_errors():
            # 初始化PyAudio
            try:
                p = create_pyaudio_instance()
            except Exception as e:
                return {}
        devices = {}
        
        try:
            for i in range(p.get_device_count()):
                device_info = p.get_device_info_by_index(i)
                if device_info['maxOutputChannels'] > 0:
                    devices[i] = device_info['name']
        finally:
            p.terminate()
            
        return devices
        
    def get_status(self) -> Dict[str, Any]:
        """
        获取播放器状态信息
        Get player status information
        
        Returns:
            Dict[str, Any]: 状态信息 / Status information
        """
        return {
            'is_playing': self.is_playing,
            'current_file': self.current_file,
            'system_sounds_count': len(self.system_sounds),
            'play_count': self.play_count,
            'total_play_time': self.total_play_time,
            'available_output_devices': self.get_available_output_devices(),
            'system_sounds': list(self.system_sounds.keys())
        }
        
    def shutdown(self):
        """
        关闭播放器
        Shutdown player
        """
        if self.is_playing:
            self.stop_playback()
            
        self.logger.info("Audio player shutdown")
        
    def __enter__(self):
        """上下文管理器入口 / Context manager entry"""
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口 / Context manager exit"""
        self.shutdown()

    def _is_valid_wav(self, filepath: str) -> bool:
        """
        校验WAV文件是否有效
        Validate whether WAV file is valid
        """
        try:
            with wave.open(filepath, 'rb') as wf:
                channels = wf.getnchannels()
                sample_width = wf.getsampwidth()
                framerate = wf.getframerate()
                # 读取少量帧以触发潜在解析错误
                _ = wf.readframes(min(wf.getnframes(), 1))
                if channels < 1 or sample_width not in (1, 2, 3, 4) or framerate <= 0:
                    return False
            return True
        except Exception:
            return False

    def _ensure_system_sound(self, sound_name: str) -> bool:
        """
        确保指定的系统提示音存在且有效，不存在或损坏则自动生成
        Ensure specified system sound exists and is valid; generate if missing or corrupted
        """
        try:
            os.makedirs(self.system_sounds_dir, exist_ok=True)
            filepath = os.path.join(self.system_sounds_dir, f"{sound_name}.wav")
            # 已存在且有效
            if os.path.exists(filepath) and self._is_valid_wav(filepath):
                self.system_sounds[sound_name] = filepath
                return True
            
            generators = {
                'beep': lambda p: create_beep(p),
                'success': lambda p: create_success_sound(p),
                'error': lambda p: create_error_sound(p),
                'wake_up': lambda p: create_wake_sound(p),
                'start_record': lambda p: create_beep(p, frequency=1200, duration=0.2),
                'stop_record': lambda p: create_beep(p, frequency=800, duration=0.2),
            }
            gen = generators.get(sound_name)
            if gen is None:
                return False
            
            gen(filepath)
            if self._is_valid_wav(filepath):
                self.system_sounds[sound_name] = filepath
                self.logger.info(f"Regenerated system sound: {sound_name}")
                return True
            else:
                self.logger.error(f"Regenerated WAV still invalid: {filepath}")
                return False
        except Exception as e:
            self.logger.error(f"Error ensuring system sound '{sound_name}': {e}")
            return False